import ab from './arraybuffer';

export class Im {

    /**
     * 事件
     */
    static EVENT = {
        /**
         * 连接关闭
         */
        CLOSE: "CLOSE",
        /**
         * sdk 就绪
         */
        READY: "READY",
        /**
         * sdk 未就绪
         */
        NOT_READY: "NOT_READY",
        /**
         * 收到消息事件
         */
        MESSAGE_RECEIVED: "MESSAGE_RECEIVED",
        /**
         * 好友申请事件
         */
        FRIENDS_APPLY: "FRIENDS_APPLY",
        /**
         * 好友申请结果事件
         */
        FRIENDS_APPLY_RESULT: "FRIENDS_APPLY_RESULT",
        /**
         * 群解散事件
         */
        GROUP_DISSOLVE: "GROUP_DISSOLVE",
        /**
         * 会话列表刷新事件
         */
        SESSION_LIST_REFRESH: "SESSION_LIST_REFRESH"
    }

    /**
     * 命令
     */
    static COMMAND = {
        DISCONNECT: 1,
        HEARTBEAT: 2,
        LOGIN: 5,
        LOGOUT: 6,
        EVENT: 10
    }

    /**
     * 协议
     */
    static protocol = {
        //魔数
        magicNumber: 21544914,
        //版本号
        version: 1,
        //最小版本号 >=
        minVersion: 1,
        //最大版本号 < 2代表不支持2版本，最多到1.99
        maxVersion: 2,
        // 0 代表json，目前只支持json
        dataType: 0,
    }

    constructor() {
        this.config = {};
        this.websocket;
        this.idleInterval;
        this.status = {
            //连接是否打开
            open: false,
            //SDK状态是否就绪 （牵扯到消息发送）
            ready: false
        };
        this.onEvent = {};
    }


    /**
     * 创建实例
     * @param {*} config 配置
     * @returns Im 
     */
    create(config) {
        if (!window.WebSocket) {
            alert('抱歉，您的浏览器不支持WebSocket，请更换相关支持的浏览器后使用本应用');
        }
        this.config = config;
        this.websocket = new WebSocket(this.config.url, 'IM');
        //监听连接打开
        this.websocket.addEventListener('open', () => {
            this.status.open = true;
            this.resetIdleInterval();
        });
        //监听连接关闭
        this.websocket.addEventListener('close', () => {
            this.status.open = false;
            this.clearIdleInterval();
            let onEvent = this.onEvent['CLOSE'];
            if (onEvent) {
                onEvent();
            }
        });
        //监听收到消息
        this.websocket.addEventListener('message', (data) => {
            this.decodeProtocol(data.data).then(protocol => {
                this.messageProcessed(protocol);
            }).catch(err => {
                console.error(err);
            })
        });
        return this;
    }

    /**
     * 登录
     * @param {*} user {u,p}
     * @returns Promise
     */
    login(user) {
        return new Promise((resolve, reject) => {
            if (!this.status.open) {
                reject('连接未打开');
            }
            this.websocket.send(this.createProtocol(Im.COMMAND.LOGIN, user));
            resolve();
        });
    }

    /**
     * 退出
     * @returns Promise
     */
    logout() {
        return new Promise((resolve, reject) => {
            if (!this.status.open) {
                reject('连接未打开');
            }
            this.websocket.send(this.createProtocol(Im.COMMAND.LOGOUT));
            resolve();
        });
    }

    /**
     * 监听时间
     * @param {*} eventName 时间名称
     * @param {*} fun 处理函数
     */
    on(eventName, fun) {
        this.onEvent[eventName] = fun;
    }

    /**
     * 发送消息
     * @param {Object} message 消息体
     * @returns Promise
     */
    sendMessage(message) {
        return new Promise((resolve, reject) => {
            if (!this.status.open) {
                reject("连接未打开");
            }
            if (!this.status.ready) {
                reject("SDK未就绪，请先登录");
            }
            this.websocket.send(message);
            resolve();
        });
    }

    /**
     * 消息解析
     * @param {*} protocol 
     */
    messageProcessed(protocol) {
        let command = protocol.command;
        //只监听处理事件命令，其余命令不处理
        if (Im.COMMAND.EVENT === command) {
            //得到消息体
            let message = protocol.content;
            //事件通知名称
            let event = message.event;
            if (event === Im.EVENT.READY) {
                this.status.ready = true;
            }
            if (event === Im.EVENT.NOT_READY) {
                this.status.ready = false;
            }
            let onEvent = this.onEvent[event];
            if (onEvent) {
                onEvent(message.data);
            }
        }
    }

    /**
     * 销毁实例
     * @returns Promise
     */
    destroy() {
        return new Promise((resolve, reject) => {
            this.logout().then(() => {
                this.websocket.send(this.createProtocol(Im.COMMAND.DISCONNECT));
            }).catch(err => reject(err));
        });
    }

    /**
     * 重置心跳定时器，15秒发送一次
     */
    resetIdleInterval() {
        this.idleInterval = setInterval((than) => {
            if (than.status.open) {
                this.websocket.send(than.createProtocol(Im.COMMAND.HEARTBEAT));
            }
        }, 1000 * 15, this);
    }

    /**
     * 移除心跳定时器
     */
    clearIdleInterval() {
        if (this.idleInterval) {
            clearInterval(this.idleInterval);
        }
    }

    /**
     * 解析协议
     * @param {*} arraybuffer 二进制数据
     * @returns protocol
     */
    async decodeProtocol(arraybuffer) {
        if (arraybuffer instanceof Blob) {
            arraybuffer = await arraybuffer.arrayBuffer();
        }
        if (!(arraybuffer instanceof ArrayBuffer)) {
            return Promise.reject('arraybuffer typeof not ArrayBuffer，is ' + arraybuffer);
        }
        let protocol = {};
        //魔数验证
        if (arraybuffer.byteLength > 4) {
            let magic = new DataView(arraybuffer.slice(0, 4));
            if (magic.getInt32() != Im.protocol.magicNumber) {
                // eslint-disable-next-line no-undef
                reject('magicNumber error，is ' + magicNumber);
            }
            protocol.magicNumber = magic.getInt32();
        }
        //版本号
        if (arraybuffer.byteLength > 5) {
            let version = new DataView(arraybuffer.slice(4, 5)).getInt8();
            if (version < Im.protocol.minVersion || version >= Im.protocol.maxVersion) {
                return Promise.reject('version error，is ${magicNumber} support min ${Im.protocol.minVersion} max ${Im.protocol.maxVersion}');
            }
            protocol.version = version;
        }
        //数据类型
        if (arraybuffer.byteLength > 6) {
            let contentType = new DataView(arraybuffer.slice(5, 6)).getInt8();
            if (contentType < Im.protocol.contentType) {
                return Promise.reject('contentType error，is ${contentType} support ${Im.protocol.contentType}');
            }
            protocol.contentType = contentType;
        }
        //数据类型
        if (arraybuffer.byteLength > 7) {
            let command = new DataView(arraybuffer.slice(6, 7)).getInt8();
            if (Im.COMMAND[command]) {
                return Promise.reject('command error，is ${command} please check the relevant documentation');
            }
            protocol.command = command;
        }
        //数据长度
        if (arraybuffer.byteLength > 11) {
            let contentLength = new DataView(arraybuffer.slice(7, 11)).getInt32();
            protocol.contentLength = contentLength;
        }
        if (arraybuffer.byteLength >= (protocol.contentLength + 11)) {
            let content = arraybuffer.slice(11, protocol.contentLength + 11);
            protocol.content = ab.arrayBufferToJson(content);
            return Promise.resolve(protocol);
        }
        return Promise.reject('protocol format error');
    }

    /**
     * 创建协议交互命令 （使用UTF-8编码）
     * @param {type} com	命令 
     * @param {Object} message 消息体
     * @returns ArrayBuffer
     */
    createProtocol(com, message) {
        let res = new ArrayBuffer(0);
        if (message) {
            if (typeof message === 'string') {
                res = ab.stringToArrayBuffer(message);
            } else {
                res = ab.jsonToArrayBuffer(message)
            }
        }
        let arrayBuffer = new ArrayBuffer(res.byteLength + 11);
        let buffer = new DataView(arrayBuffer);
        //魔数
        buffer.setInt32(0, Im.protocol.magicNumber);
        //版本号
        buffer.setUint8(4, Im.protocol.version);
        //数据类型
        buffer.setUint8(5, Im.protocol.contentType);
        //命令
        buffer.setUint8(6, com);
        //数据长度
        buffer.setInt32(7, res.byteLength);
        //循环填充数据体
        let dataView = new DataView(res);
        for (let i = 0; i < res.byteLength; i++) {
            buffer.setInt8(11 + i, dataView.getInt8(i));
        }
        return arrayBuffer;
    }

}
